package cc.shacocloud.kotlin.tools.http

import okhttp3.OkHttpClient
import org.jetbrains.annotations.Contract
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.*

/**
 * 忽略TLS握手工具
 *
 * @author 思追(shaco)
 */
object IgnoreTLSHandshakeUtil {

    /**
     * X509TrustManager 忽略 SSL 认证的实例
     */
    private val IGNORE_SSL_TRUST_MANAGER_X509: X509TrustManager = object : X509TrustManager {
        override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        @Contract(value = " -> new", pure = true)
        override fun getAcceptedIssuers(): Array<X509Certificate> {
            return arrayOf()
        }
    }


    /**
     * 构建一个新的忽略TLS握手的 OkHttpClient
     *
     * @param client 旧的 OkHttpClient
     */
    @Contract("_ -> new")
    @Throws(RuntimeException::class)
    fun builder(client: OkHttpClient): OkHttpClient {
        try {
            return client.newBuilder()
                .sslSocketFactory(ignoreInitedSslContext.socketFactory, IGNORE_SSL_TRUST_MANAGER_X509)
                .hostnameVerifier(ignoreSslHostnameVerifier)
                .build()
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }

    @get:Throws(NoSuchAlgorithmException::class, KeyManagementException::class)
    private val ignoreInitedSslContext: SSLContext
        /**
         * 获取忽略 SSL 认证的初始化 SSLContext 实例
         */
        get() {
            val sslContext = SSLContext.getInstance("TLS")
            sslContext.init(null, arrayOf<TrustManager>(IGNORE_SSL_TRUST_MANAGER_X509), SecureRandom())
            return sslContext
        }

    @get:Contract(pure = true)
    private val ignoreSslHostnameVerifier: HostnameVerifier
        /**
         * 获取忽略 SSL 认证的主机名验证程序
         */
        get() = HostnameVerifier { _: String, _: SSLSession -> true }
}
